#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/sysctl.h>
#include <linux/stat.h>
#include "version.h"
#include "sysctl.h"
#include "kprobe_prehook.h"
#include "data_aware.h"
#include "hook.h"

static int one_hundred = 100;
static int sysctl_two = 2;
static int one_thousand = 1000;
static int max = 1000000000;

#if defined(TK3) || defined(TK2)

/* shared constants to be used in various sysctls */
const int sysctl_vals[] = { 0, 1, INT_MAX };

/* Keep the same order as in fs/proc/proc_sysctl.c */
#define SYSCTL_ZERO     ((void *)&sysctl_vals[0])
#define SYSCTL_ONE      ((void *)&sysctl_vals[1])
#define SYSCTL_INT_MAX  ((void *)&sysctl_vals[2])

#endif

/* numa_aware sysctl variables */
extern unsigned int sysctl_module_enable;
extern unsigned int sysctl_module_debug;
extern unsigned int sysctl_kprobe_unregister;
extern unsigned int sysctl_module_block_enable;
extern int register_ftrace_ftrace;
extern char sysctl_module_process_comm[NAME_MAX];

extern int sysctl_numa_enable_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_kretprobe_enable_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_kretprobe_disable_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_get_data_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_clear_data_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_get_func_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_ftrace_hook_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_ftrace_unhook_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_system_hook_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_system_unhook_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);
extern int sysctl_ftrace_func_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos);

struct func_latency data_latency;
static struct ctl_table os_aware_table[] = {
	{
		.procname	= "enable",
		.data		= &sysctl_module_enable,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= sysctl_numa_enable_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &sysctl_two,
	},
	{
		.procname	= "control_stat",
		.data		= &sysctl_module_debug,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= sysctl_clear_data_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "proc_comm",
		.data		= &sysctl_module_process_comm,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= proc_dostring,
	},
	{
		.procname	= "enable_stat_block",
		.data		= &sysctl_module_block_enable,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "debug_to_print",
		.data		= &sysctl_module_print,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "ftrace_success",
		.data		= &register_ftrace_ftrace,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "kret_probe_success",
		.data		= &register_kretprobe_ftrace,
		.maxlen		= sizeof(unsigned int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "data",
		.data		= &sysctl_data,
		.maxlen		= sizeof(sysctl_data),
		.mode		= 0444,
		.proc_handler	= sysctl_get_data_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_thousand,
	},
	{
		.procname	= "func_data",
		.data		= &data_latency,
		.maxlen		= sizeof(struct func_latency),
		.mode		= 0444,
		.proc_handler	= sysctl_get_func_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_thousand,
	},
	{
		.procname	= "ftrace_hook_one_function",
		.data		= &ftrace_hook_name,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= sysctl_ftrace_func_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "ftrace_hook_function",
		.data		= &ftrace_hook_name,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= sysctl_ftrace_hook_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "ftrace_unhook_function",
		.data		= &ftrace_hook_name,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= sysctl_ftrace_unhook_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "system_hook_function",
		.data		= &system_hook_name,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= sysctl_system_hook_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "system_unhook_function",
		.data		= &system_hook_name,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= sysctl_system_unhook_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "printk_struct_first_name",
		.data		= &printk_name_first,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= proc_dostring,
	},
	{
		.procname	= "printk_struct_last_name",
		.data		= &printk_name_last,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= proc_dostring,
	},
	{
		.procname	= "write_func_pointer",
		.data		= &func_pointer,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= proc_dostring,
	},
	{
		.procname	= "get_func_pointer",
		.data		= &func_pointer_name,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= proc_dostring,
	},
	{
		.procname	= "kprobe_register_func",
		.data		= &symbol_new,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= sysctl_kretprobe_enable_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "kprobe_unregister_func",
		.data		= &symbol_kret_new,
		.maxlen		= NAME_MAX * sizeof(char),
		.mode		= 0644,
		.proc_handler	= sysctl_kretprobe_disable_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &one_hundred,
	},
	{ }
};
#if defined(TK4_OLD) || defined(TK4_NEW) || defined(TK3)
static struct ctl_table numa_aware_root_table[] = {
	{
		.procname	= "os_aware",
		.maxlen		= 0,
		.mode		= S_IRUGO|S_IXUGO,
		.child		= os_aware_table,
	},
	{  }
};
#endif

static struct ctl_table_header *module_root;

int sysctl_table_init(void)
{
	/* Register sysctl */
#ifdef TK5
	module_root = register_sysctl_sz("os_aware", os_aware_table, ARRAY_SIZE(os_aware_table));
#else
	module_root = register_sysctl_table(numa_aware_root_table);
	if (!module_root)
		return -ENOMEM;
#endif

	return 0;
}

void sysctl_table_exit(void)
{
	/* Unregister sysctl */
	unregister_sysctl_table(module_root);
}
